NOIP2012 普及组

T1:质因数分解

题目描述

已知正整数 n 是两个不同的质数的乘积,试求出两者中较大的那个质数。

输入格式

一个正整数 n

输出格式

一个正整数 p ,即较大的那个质数。

输入输出样例

输入样例 #1

21

输出样例 #1

7

说明/提示

n\le 2\times 10^9

NOIP 2012 普及组 第一题

题解:

​​​ ​读入一个数字 n 之后,为了减少时间复杂度,首先将 n 开一下根,然后从 2 开始枚举 n 的质因数。唯一分解定理告诉我们,一个数只能分解为一组质数的乘积,所以,我们从从 2 sqrt(n) 来开始枚举 n 的质因数,如果发现 n\ mod\ i\ =\ 0 之后,便得到了一个较小的质因数 i 。为了得到较大的那一个,输出 n\ ÷\ i 就可以了。

#include <cmath>
#include <cstdio>
using namespace std;
int main() {
    long long n;
    scanf("%lld", &n);
    int l = sqrt(n) + 1;
    for (long long i = 2; i <= l; i++)
        if (n % i == 0) {
            printf("%lld\n", n / i);
            return 0;
        }
    return 0;
}

T2:寻宝

题目描述

传说很遥远的藏宝楼顶层藏着诱人的宝藏。小明历尽千辛万苦终于找到传说中的这个藏宝楼,藏宝楼的门口竖着一个木板,上面写有几个大字:寻宝说明书。说明书的内容如下:

藏宝楼共有 N+1 层,最上面一层是顶层,顶层有一个房间里面藏着宝藏。除了顶层外,藏宝楼另 0 N 层,每层 M 个房间,这 M 个房间围成一圈并按逆时针方向依次编号为 0,…,M-1 。其中一些房间有通往上一层的楼梯,每层楼的楼梯设计可能不同。每个房间里有一个指示牌,指示牌上有一个数字 x ,表示从这个房间开始按逆时针方向选择第 x 个有楼梯的房间(假定该房间的编号为k),从该房间上楼,上楼后到达上一层的 k 号房间。比如当前房间的指示牌上写着 2 ,则按逆时针方向开始尝试,找到第 2 个有楼梯的房间,从该房间上楼。如果当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。

寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥”。 请帮助小明算出这个打开宝箱的密钥。

输入格式

第一行 2 个整数 N M ,之间用一个空格隔开。 N 表示除了顶层外藏宝楼共 N 层楼, M 表示除顶层外每层楼有 M 个房间。

接下来 N \times M 行,每行两个整数,之间用一个空格隔开,每行描述一个房间内的情况,其中第 (i-1) \times M+j 行表示第 i j-1 号房间的情况( i=1,2,…, N j=1,2,…,M )。第一个整数表示该房间是否有楼梯通往上一层( 0 表示没有, 1 表示有),第二个整数表示指示牌上的数字。注意,从 j 号房间的楼梯爬到上一层到达的房间一定也是 j 号房间。

最后一行,一个整数,表示小明从藏宝楼底层的几号房间进入开始寻宝(注:房间编号从 0 开始)。

输出格式

一个整数,表示打开宝箱的密钥,这个数可能会很大,请输出对 20123 取模的结果即可。

输入输出样例

输入样例 #1

2 3
1 2
0 3
1 4
0 1
1 5
1 2
1

输出样例 #1

5

说明/提示

【数据范围】

对于50%数据,有 0<N≤1000,0<x≤10000

对于100%数据,有 0<N≤10000,0<M≤100,0<x≤1,000,000

NOIP 2012 普及组 第二题

题解:

​​​ ​本题就是一道模拟题,但是题目条件比较多,比较考察读题能力。

​​​ ​由于指示牌上的数字可能会很大,需要围着本层楼跑好几圈才能够跑到能像上一层的房间,所以,我们设置一个 Floor 数组存储每层楼中有楼梯的房间的总数。然后,将指示牌上的数字 mod 一下对应的 Floor ,就可以保证在一圈之内找到通往上一层的入口。

#include <iostream>
#define MOD 20123
using namespace std;
int room[10001][101][4];
int Floor[100001];
int main() {
    int n, m, start;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < m; j++) {
            cin >> room[i][j][1] >> room[i][j][2];
            Floor[i] += room[i][j][1];
        }
    cin >> start;
    int ans = 0; //记录答案
    for (int i = 1; i <= n; i++) { //从第一层楼开始向上爬
        ans = (ans + room[i][start][2]) % MOD; //记录答案
        int cs = 0, q = start;
        room[i][start][2] = (room[i][q][2] - 1) % Floor[i] + 1; //剪枝,保证一圈之内找到入口
        while (cs < room[i][q][2]) {
            cs += room[i][start][1];
            if (cs == room[i][q][2])
                break;
            start++;
            if (start == m)
                start = 0;
        }
    }
    cout << ans << endl;
    return 0;
}

T3:摆花

题目描述

小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 n 种花,从 1 n 标号。为了在门口展出更多种花,规定第 i 种花不能超过 a_i 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。

试编程计算,一共有多少种不同的摆花方案。

输入格式

第一行包含两个正整数 n m ,中间用一个空格隔开。

第二行有 n 个整数,每两个整数之间用一个空格隔开,依次表示 a_1,a_2,…,a_n

输出格式

一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对 1000007 取模的结果。

输入输出样例

输入样例 #1

2 4
3 2

输出样例 #1

2

说明/提示

【数据范围】

对于20%数据,有 0<n≤8,0<m≤8,0≤a_i≤8

对于50%数据,有 0<n≤20,0<m≤20,0≤a_i≤20

对于100%数据,有 0<n≤100,0<m≤100,0≤a_i≤100

NOIP 2012 普及组 第三题

题解:

​​​ ​本题是一个动态规划问题,但是,本题却不需要 min max 函数,而是只需要简单地从上一个状态转移到当前状态。

​​​ ​我们设置一个 DP 数组,用 DP[j] 来表示当摆上 j 盆花时有多少种不同的摆花方案。

​​​ ​然后,我们用 i 来枚举每盆花,用 j 来枚举摆的花的数量,用 j\ -\ k 来枚举上一个状态,便可以得到 DP[j]\ =\ DP[j]\ +\ DP[j\ -\ k] 的动态转移方程,最后,输出 DP[m] 就可以辣!

#include <iostream>
#define MOD 1000007
using namespace std;
int a[101], DP[101];
int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    DP[0] = 1;
    for (int i = 1; i <= n; i++) //从第一盆花开始枚举
        for (int j = m; j >= 0; j--)
            for (int k = 1; k <= min(j, a[i]); k++)
                DP[j] = (DP[j] + DP[j - k]) % MOD;
    cout << DP[m] << endl;
    return 0;
}

T4:文化之旅

题目描述

有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超过一次(即如果他学习了某种文化,则他就不能到达其他有这种文化的国家)。不同的国家可能有相同的文化。不同文化的国家对其他文化的看法不同,有些文化会排斥外来文化(即如果他学习了某种文化,则他不能到达排斥这种文化的其他国家)。

现给定各个国家间的地理关系,各个国家的文化,每种文化对其他文化的看法,以及这位使者游历的起点和终点(在起点和终点也会学习当地的文化),国家间的道路距离,试求从起点到终点最少需走多少路。

输入格式

第一行为五个整数 N,K,M,S,T ,每两个整数之间用一个空格隔开,依次代表国家个数(国家编号为 1 N ),文化种数(文化编号为 1 K ),道路的条数,以及起点和终点的编号(保证 S 不等于 T );

第二行为 N 个整数,每两个整数之间用一个空格隔开,其中第 i 个数 C_i ,表示国家 i 的文化为 C_i

接下来的 K 行,每行 K 个整数,每两个整数之间用一个空格隔开,记第 i 行的第 j 个数为 a_{ij} a_{ij}= 1 表示文化 i 排斥外来文化 j i 等于 j 时表示排斥相同文化的外来人), a_{ij}= 0 表示不排斥(注意 i 排斥 j 并不保证 j 一定也排斥 i )。

接下来的 M 行,每行三个整数 u,v,d ,每两个整数之间用一个空格隔开,表示国家 u 与国家 v 有一条距离为 d 的可双向通行的道路(保证 u 不等于 v ,两个国家之间可能有多条道路)。

输出格式

一个整数,表示使者从起点国家到达终点国家最少需要走的距离数(如果无解则输出 -1 )。

输入输出样例

输入样例 #1

2 2 1 1 2 
1 2 
0 1 
1 0 
1 2 10 

输出样例 #1

-1

输入样例 #2

2 2 1 1 2 
1 2 
0 1 
0 0 
1 2 10 

输出样例 #2

10

说明/提示

输入输出样例说明 1

由于到国家 2 必须要经过国家 1 ,而国家 2 的文明却排斥国家 1 的文明,所以不可能到达国家 2

输入输出样例说明 2

路线为 1 -> 2

【数据范围】

对于 100%的数据,有 2≤N≤100

1≤K≤100

1≤M≤N^2

1≤k_i≤K

1≤u, v≤N

1≤d≤1000,S≠T,1≤S,T≤N

NOIP 2012 普及组 第四题

题解:

​​​ ​Emmm......由于本题被证明没有靠谱的多项式复杂度的做法,所以没有正解。那么。。。就怎么玄学怎么来呗!

​​​ ​我采用了 Floyd() 求最短路的方法。我们用 road[i][j] 表示 i 国家到 j 国家的最短路,并将所有的 road 赋值为正无穷。读入每条路径,将双向通行的道路转为单向通行的道路:判断这条路所联通的两个国家 u v 的文化是否互相排斥。如果 u 不排斥 v 的话则添加 u\ ->\ v 的道路, v 不排斥 u 的话则添加 v\ ->\ u 的道路。至此,初始化完毕。

​​​ ​ Floyd () 函数里面就是三重 for() 循环。用变量 i,\ j,\ k 枚举从 i\ ->\ j 途径 k 的路径。如果 i\ ->\ k\ ,\ k\ ->\ j 之间有路的话,那么, road[i][j]\ =\ min(road[i][j],\ (road[i][k]\ +\ road[k][j])) 。最后,判断一下 road[s][t] 是否小于正无穷,小于的话输出 road[s][t] ,否则输出 -1

#include <cstring>
#include <iostream>
using namespace std;
int c[101];
int exclude[101][101];
int n, k, m, s, t;
int road[101][101];
int pass[101][101];
void Floyd () {
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (i != j)
                for (int k = 1; k <= n; k++)
                    if (i != k && j != k)
                        road[i][j] = min(road[i][j], (road[i][k] + road[k][j]));
}
int main() {
    memset(road, 0x3f3f3f, sizeof(road));
    cin >> n >> k >> m >> s >> t;
    for (int i = 1; i <= n; i++)
        cin >> c[i];
    if(c[s] == c[t]){
        cout << -1;
        return 0;
    }
    for (int i = 1; i <= k; i++)
        for (int j = 1; j <= k; j++)
            cin >> exclude[i][j];
    for (int i = 1; i <= m; i++) {
        int u, v, d;
        cin >> u >> v >> d;
        if (c[u] != c[v] && exclude[c[v]][c[u]] == 0) //两个国家文化不同且不互相排斥
            road[u][v] = min(road[u][v], d); // road 表示从 u 到 v 的最小路,所以要先判断 v 文化是否排斥 u 文化,
        if (c[u] != c[v] && exclude[c[u]][c[v]] == 0)
            road[v][u] = min(road[v][u], d);
    }
    Floyd();
    if (road[s][t] < 0x3f3f3f)
        cout << road[s][t] << endl;
    else
        cout << -1 << endl;
    return 0;
}